[Swift]イヤホン(ヘッドホン)が抜けて音声が外に漏れる事故がないように、接続状態をきちんと監視しよう
はじめに
CX事業本部の中安です。まいどです。
以前に「シンプルなオーディオプレイヤーを作ってみよう」という記事で タイトル通りオーディオプレイヤーアプリを作ってみました。
今回はその続きとなる内容になります。
ここで作成したオーディオプレイヤーアプリには "ある欠陥" がありました。 それはイヤホンで音楽を聞いていると、そのイヤホンが抜けてしまっても音が鳴り続けてしまうことでした。
たいていのオーディオプレイヤーアプリは、そういうときは音声再生が一時停止して、 周りに聞いている音楽が聞かれるという恥ずかしいことは起きないのですが、 それは自動的に止まるのではなく、制御を入れてやらなくてはいけないのです。
イベントを取る
実件方法はシンプルです。
AVFoundation
をインポートし、AVAudioSession.routeChangeNotification
を NotificationCenter
経由で監視してやるだけです。
実装例を以下のような感じです。
// ビューコントローラであれば viewDidLoad。その他クラスであれば init() とかに書く NotificationCenter.default.addObserver( self, selector: #selector(didAudioSessionRouteChange(_:)), name: AVAudioSession.routeChangeNotification, object: nil )
セレクタとして定義したメソッドも実装します。
@objc private func didAudioSessionRouteChange(_ notification: Notification) { // 後述します )
今定義した didAudioSessionRouteChange(_:)
は、イヤホンが「刺された時」も「抜かれた時」も同じ箇所が呼ばれます。
そのため、それを判別するためにはイベント通知の中身を見る必要があります。
@objc private func didAudioSessionRouteChange(_ notification: Notification) { guard let userInfo = notification.userInfo, let routeChangeReasonRawValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt, let routeChangeReason = AVAudioSession.RouteChangeReason(rawValue: routeChangeReasonRawValue) else { return } :
上記ソースコードのように RouteChangeReason
という列挙型のオブジェクトを userInfo
から取得します。
そして、それをswitch文でケース分けしてやります。
switch routeChangeReason { // イヤホンなどが刺された時 case .newDeviceAvailable: // イヤホンなどが抜かれた時 case .oldDeviceUnavailable: default: break }
最終的にはAVAudioSessionPortDescription
というオブジェクトに詳細な情報が入ってくるのですが、
イヤホンなどが刺された時と抜かれた時で、少しそのオブジェクトの取り方も変わるようなので、そのあたりもご紹介します。
イヤホンが刺された時のイベント
イヤホンが刺されたときは、AVAudioSession.sharedInstance().currentRoute.outputs
という配列に情報が入ってきます。
「イヤホンが刺された」と書いていますが、それ以外にも接続する機器があるかもしれませんので、 portType
というプロパティで接続された機器を判別することもできます。
取得できる機器はこちらを参照ください。
今回は刺されたことが取れればいいので、このようにしました。
guard let output = AVAudioSession.sharedInstance().currentRoute.outputs.first else { return } print("\(output.portName)が刺された")
イヤホンが抜かれた時のイベント
イヤホンが抜かれた時は、userInfo
から AVAudioSessionRouteDescription
というオブジェクトを抜き出し、
その中から先ほど同様にAVAudioSessionPortDescription
を取得することができます。
// userInfo は前述で guard let してるものとします guard let previousRoute = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription, let output = previousRoute.outputs.first else { return } print("\(output.portName)が抜かれた")
print
を実行してるタイミングで音声再生を一時停止してやれば、イヤホンが抜けてしまった時に音が周りに聞こえてしまうことがなくなるでしょう。
おわりに
AVFoundation
を使用した音声を扱うアプリであれば、接続機器に関するユーザのケアも必要になってくると思いますので、
ぜひご参考になさってください。
それではまたー